package com.mythesis.bpel.ui.client.dnd;

import java.util.ArrayList;

import com.allen_sauer.gwt.dnd.client.DragContext;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.AbstractPositioningDropController;
import com.allen_sauer.gwt.dnd.client.util.DOMUtil;
import com.allen_sauer.gwt.dnd.client.util.WidgetLocation;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.mythesis.bpel.ui.client.controls.BpelControl;
import com.mythesis.bpel.ui.client.wrapper.ComponentControlWrapper;
import com.mythesis.bpel.ui.client.wrapper.ControlWrapper;
import com.mythesis.bpel.ui.client.wrapper.SequenceControlItemWrapper;

public class BpelDropController extends AbstractPositioningDropController {

	public static class Draggable {
		public int desiredX;
		public int desiredY;
		public int relativeX;
		public int relativeY;
		public final int offsetHeight;
		public final int offsetWidth;
		public Widget positioner = null;
		public final Widget widget;

		public Draggable(Widget widget) {
			this.widget = widget;
			offsetWidth = widget.getOffsetWidth();
			offsetHeight = widget.getOffsetHeight();
		}
	}

	protected static final Label DUMMY_LABEL_IE_QUIRKS_MODE_OFFSET_HEIGHT = new Label(
			"x");

	protected static final String CSS_DRAGDROP_POSITIONER = "dragdrop-positioner";

	final ArrayList<Draggable> draggableList = new ArrayList<Draggable>();
	final AbsolutePanel dropTarget;
	int dropTargetClientHeight;
	int dropTargetClientWidth;
	int dropTargetOffsetX;
	int dropTargetOffsetY;

	public BpelDropController(AbsolutePanel dropTarget) {
		super(dropTarget);
		this.dropTarget = dropTarget;
		// TODO Auto-generated constructor stub
	}

	/**
	 * Programmatically drop a widget on our drop target while obeying the
	 * constraints of this controller.
	 * 
	 * @param widget
	 *            the widget to be dropped
	 * @param left
	 *            the desired absolute horizontal location relative to our drop
	 *            target
	 * @param top
	 *            the desired absolute vertical location relative to our drop
	 *            target
	 */
	public void drop(Widget widget, int left, int top) {
		left = Math.max(0, Math.min(left, dropTarget.getOffsetWidth()
				- widget.getOffsetWidth()));
		top = Math.max(0, Math.min(top, dropTarget.getOffsetHeight()
				- widget.getOffsetHeight()));
		dropTarget.add(widget, left, top);
	}

	@Override
	public void onDrop(DragContext context) {
		for (Draggable draggable : draggableList) {
			draggable.positioner.removeFromParent();

			/*
			 * dropTarget.add(draggable.widget, draggable.desiredX,
			 * draggable.desiredY);
			 */

			BpelControl control = (BpelControl) draggable.widget;
			//SequenceControlItemWrapper wrapper = new SequenceControlItemWrapper(control);
			//ComponentControlWrapper wrapper = new ComponentControlWrapper(control);
			ControlWrapper wrapper = control.getControlWrapper();
			if(wrapper!= null){
				wrapper.renderControl();
				dropTarget.add(wrapper, draggable.desiredX, draggable.desiredY);
				context.dragController.makeNotDraggable(draggable.widget);
			}
			else
				GWT.log(control.toString(),null);
			
		}

		super.onDrop(context);
	}

	@Override
	public void onEnter(DragContext context) {
		super.onEnter(context);
		assert draggableList.size() == 0;

		dropTargetClientWidth = DOMUtil.getClientWidth(dropTarget.getElement());
		dropTargetClientHeight = DOMUtil.getClientHeight(dropTarget
				.getElement());
		WidgetLocation dropTargetLocation = new WidgetLocation(dropTarget, null);
		dropTargetOffsetX = dropTargetLocation.getLeft()
				+ DOMUtil.getBorderLeft(dropTarget.getElement());
		dropTargetOffsetY = dropTargetLocation.getTop()
				+ DOMUtil.getBorderTop(dropTarget.getElement());

		int draggableAbsoluteLeft = context.draggable.getAbsoluteLeft();
		int draggableAbsoluteTop = context.draggable.getAbsoluteTop();
		for (Widget widget : context.selectedWidgets) {
			Draggable draggable = new Draggable(widget);
			draggable.positioner = makePositioner(widget);
			draggable.relativeX = widget.getAbsoluteLeft()
					- draggableAbsoluteLeft;
			draggable.relativeY = widget.getAbsoluteTop()
					- draggableAbsoluteTop;
			draggableList.add(draggable);
		}
	}

	@Override
	public void onLeave(DragContext context) {
		for (Draggable draggable : draggableList) {
			draggable.positioner.removeFromParent();
		}
		draggableList.clear();
		super.onLeave(context);
	}

	@Override
	public void onMove(DragContext context) {
		super.onMove(context);
		for (Draggable draggable : draggableList) {
			draggable.desiredX = context.desiredDraggableX - dropTargetOffsetX
					+ draggable.relativeX;
			draggable.desiredY = context.desiredDraggableY - dropTargetOffsetY
					+ draggable.relativeY;
			draggable.desiredX = Math.max(0, Math.min(draggable.desiredX,
					dropTargetClientWidth - draggable.offsetWidth));
			draggable.desiredY = Math.max(0, Math.min(draggable.desiredY,
					dropTargetClientHeight - draggable.offsetHeight));
			dropTarget.add(draggable.positioner, draggable.desiredX,
					draggable.desiredY);
		}
	}

	Widget makePositioner(Widget reference) {
		// Use two widgets so that setPixelSize() consistently affects
		// dimensions
		// excluding positioner border in quirks and strict modes
		SimplePanel outer = new SimplePanel();
		outer.addStyleName(CSS_DRAGDROP_POSITIONER);
		outer.getElement().getStyle().setProperty("margin", "0px");

		// place off screen for border calculation
		RootPanel.get().add(outer, -500, -500);

		// Ensure IE quirks mode returns valid outer.offsetHeight, and thus
		// valid
		// DOMUtil.getVerticalBorders(outer)
		outer.setWidget(DUMMY_LABEL_IE_QUIRKS_MODE_OFFSET_HEIGHT);

		SimplePanel inner = new SimplePanel();
		inner.getElement().getStyle().setProperty("margin", "0px");
		inner.getElement().getStyle().setProperty("border", "none");
		int offsetWidth = reference.getOffsetWidth()
				- DOMUtil.getHorizontalBorders(outer);
		int offsetHeight = reference.getOffsetHeight()
				- DOMUtil.getVerticalBorders(outer);
		inner.setPixelSize(offsetWidth, offsetHeight);

		outer.setWidget(inner);

		return outer;
	}
}
